package jfilters.filters.edge;

/*
    Copyright Janne Liljeblad 2008.

    This file is part of JFilters.

    JFilters is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    JFilters is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with JFilters.  If not, see <http://www.gnu.org/licenses/>.
*/

import jfilters.filters.JFilter;
import jfilters.libmath.JFMath;
import jfilters.lib.PixelRow;
import java.util.Arrays;
import java.awt.image.*;
import java.awt.*;

public class JFLaplace extends JFilter
{
	//--- Draw params.
	private boolean horizontal = true;
	private boolean vertical = true;
	private boolean keep_sign = true;

	public JFLaplace(){}

	public BufferedImage filter( BufferedImage sourceImg, BufferedImage destinationImg )
	{
		WritableRaster destination = destinationImg.getRaster();
		WritableRaster source = sourceImg.getRaster();

		int x1 = 0;
		int y1 = 0;
		int x2 = source.getWidth();
		int y2 = source.getHeight();

		int width  = x2 - x1;
		int height = y2 - y1;

		int bytes = source.getNumBands();
		boolean alpha = ( bytes == 4 );

		PixelRow pr = prepareRow( source, y1 -1 );// wont be claped
		PixelRow cr = prepareRow( source, y1 );
		PixelRow nr;
		PixelRow dr;
		PixelRow tmp;

  		//int counter = 0;
		int row;
		int col;

		int max_gradient = 0;
		int gradient;

		IntVal minval = new IntVal();
		IntVal maxval = new IntVal();

		/*  loop through the rows, applying the laplace convolution  */
		for (row = y1; row < y2; row++)
		{
			/*  prepare the next row  */
			nr = prepareRow( source, row + 1 );
			dr = prepareRow( destination, row );

			for (col = 0; col < width * bytes; col++)
			{
				if (alpha && (((col + 1) % bytes) == 0)) /* the alpha channel */
				{
					dr.setSample( col, cr.getSample( col ) );
				}
				else
				{
					minmax (pr.getSample(col), cr.getSample(col - bytes),  cr.getSample(col),  cr.getSample(col + bytes),
						nr.getSample(col), minval, maxval); /* four-neighbourhood */

					gradient = (int) ( 0.5 * Math.max(( maxval.val - cr.getSample(col) ), ( cr.getSample(col)- minval.val)));
					max_gradient = Math.max( Math.abs( gradient ), max_gradient );
					dr.setSample( col,
							((pr.getSample(col - bytes) + pr.getSample(col) + pr.getSample(col + bytes) +
							cr.getSample(col - bytes) - (8 * cr.getSample(col)) + cr.getSample(col + bytes) +
							nr.getSample(col - bytes) + nr.getSample(col) + nr.getSample(col + bytes)) > 0) ?
							gradient : (128 + gradient)
						    );
				}
			}
			
			/*  store the dest  */
			dr.commitData();
				
			/*  shuffle the row pointers  */
			tmp = pr;
			pr = cr;
			cr = nr;
			nr = tmp;
		}


  		/* now clean up:
		 leave only edges, but keep gradient value */
		pr = prepareRow( source, y1 -1 );
		cr = prepareRow( source, y1 );


		float scale = (255.0f / (float) max_gradient);
		int current;
		int counter = 0;

		/*  loop through the rows, applying the laplace convolution  */
		for (row = y1; row < y2; row++)
		{
			/*  prepare the next row  */
			nr = prepareRow( source, row + 1 );
			dr = prepareRow( destination, row );

			for (col = 0; col < (x2 - x1) * bytes; col++)
			{
				current = cr.getSample( col );
				current = ( WHITEREGION( current ) &&
					       (BLACKREGION( pr.getSample(col - bytes) ) ||
						BLACKREGION( pr.getSample(col ) )        ||
						BLACKREGION( pr.getSample(col + bytes) ) ||
						BLACKREGION( cr.getSample(col - bytes) ) ||
						BLACKREGION( cr.getSample(col + bytes) ) ||
						BLACKREGION( nr.getSample(col - bytes) ) ||
						BLACKREGION( nr.getSample(col ) )        ||
						BLACKREGION( nr.getSample(col + bytes) ))) ?
						(int) (scale * 
								((float) ((current >= 128) ? (current-128) : current))
							) : 0;
			
				if (alpha && (((col + 1) % bytes) == 0)) /* the alpha channel */
				{
					dr.setSample( col, 255 );
					
					//dr.setSample( col, (counter == 0) ? 0 : 255 );
					counter = 0;
					
				}
				else
				{
					dr.setSample( col, current );
				}
			}
			
			/*  store the dest  */
			dr.commitData();
			
			/*  shuffle the row pointers  */
			tmp = pr;
			pr = cr;
			cr = nr;
			nr = tmp;
		}
		return destinationImg;
	}


	private void minmax(	int  x1,
				int  x2,
				int  x3,
				int  x4,
				int  x5,
				IntVal min_result,
				IntVal max_result)
	{
		int min1, min2, max1, max2;
		
		if (x1 > x2) { max1=x1; min1=x2; } else { max1=x2; min1=x1; }
		if (x3 > x4) { max2=x3; min2=x4; } else { max2=x4; min2=x3; }

		if (min1 < min2) min_result.val = Math.min(min1, x5);
		else  min_result.val = Math.min(min2, x5);

		if (max1 > max2) max_result.val = Math.max(max1, x5);
		else  max_result.val = Math.max(max2, x5);
	}


	private int SIGN( int a ){ return (a > 0) ? 1:-1; }

	//private int RMS(a,b) (sqrt (pow ((a),2) + pow ((b), 2)))

	private boolean BLACKREGION( int val ){ return val > 128; }

	private boolean WHITEREGION( int val ){ return val <= 128; }

	private PixelRow prepareRow( WritableRaster raster, int y )
	{
		y = JFMath.CLAMP( y, 0, raster.getHeight() - 1 );
		return new PixelRow( raster, y, PixelRow.CLAMP_EDGES );
	}

	//--- Used to get minmax -function working
	class IntVal
	{
		public int val;
	
		public IntVal(){ val = 0; }
	}

}//end class
